import pandas as pd
from scipy.special import boxcox1p
from sklearn import preprocessing

# ---临时函数，时间序列上纠正方差太大的离群值---
def ProcessTimeSeriesOutlier(df, name, window=20, sigmaX=2):

    #
    fieldName = name + "_diff"
    df[fieldName] = df[name].diff(1)
    stdDev = df[fieldName].std()
    mean = df[fieldName].mean()

    #
    # linreg = linear_model.LinearRegression(copy_X=True, fit_intercept=True, n_jobs=1, normalize=False)
    # model = linreg.fit(xMarketRets, yStockRets)

    #
    df[name + "_avg"] = df[name].rolling(window=window, center=True).mean()
    for index, row in df.iterrows():
        # ---离群太远---
        # if math.fabs(row[name + "_avg"] - row[name]) > sigmaX * stdDev:
            # ---Fix Value---
        if not pd.isna(row[name + "_avg"]):
            df.at[index, name] = row[name + "_avg"]
            print(row["DateTime"], "FixTo", row[name])
            pass


def Outlier_Percentile(df, lowerBound=0.025, upperBound=0.975, remove=False):
    #
    def function(x, upper, lower):
        if x < lower:
            return  lower
        if x > upper:
            return  upper
        return x

    numeric_features = list(df.columns)
    if "Symbol" in numeric_features:
        numeric_features.remove("Symbol")
    if "DateTime" in numeric_features:
        numeric_features.remove("DateTime")
    #
    for feature in numeric_features:
        qt_5 = df[feature].quantile(lowerBound)  # 5%分位数
        qt_95 = df[feature].quantile(upperBound)  # 95%分位数

        # 移除极值
        if remove:
            df = df[df[feature] > qt_5]
            df = df[df[feature] < qt_95]
        else:
            df[feature] = df.apply(lambda x: function(x[feature], qt_95, qt_5), axis=1)
            # df[feature][df[feature] < qt_5] = qt_5  # 小于5%分位数的取5%分位数
            # df[feature][df[feature] > qt_95] = qt_95  # 大于95%分位数的取95%分位数
    #
    return df


def Outlier_NSigma(df, n=3, remove=False):
    #
    miu = df.mean()
    sigma = df.std()
    upper = miu + n * sigma
    lower = miu - n * sigma
    #
    def function(x, upper, lower):
        if x < lower:
            return  lower
        if x > upper:
            return upper
        return x
    #
    numeric_features = list(df.columns)
    redundantFields = ["Symbol", "DateTime"]
    for field in redundantFields:
        if field in numeric_features:
            numeric_features.remove(field)

    #
    for feature in numeric_features:
        # 移除极值
        if remove:
            df = df[df[feature] <= upper[feature]]
            df = df[df[feature] >= lower[feature]]
        else:
            df[feature] = df.apply(lambda x: function(x[feature], upper[feature], lower[feature]), axis=1)
            # df[[feature]][df[feature] < lower[feature]] = lower[feature]
            # df[[feature]][df[feature] > upper[feature]] = upper[feature]
            # pass
    # print(df)
    return df


def Boxcox(data_df):
    # ---boxcox转化, 正态校正---
    numeric_features = list(data_df.columns)
    for feature in numeric_features:
       # all_data[feat] += 1
       data_df[feature] = boxcox1p(data_df[feature], 0.15)


def Normalization(data_df, method=None):

    # ---Normalization---
    numeric_features = list(data_df.columns)
    redundantFields = ["Symbol", "DateTime"]
    for field in redundantFields:
        if field in numeric_features:
            numeric_features.remove(field)

    #
    if method == "ZScore":
        #
        data_df[numeric_features] = preprocessing.scale(data_df[numeric_features])
    elif method == "MaxMin":
        #
        scaler = preprocessing.MinMaxScaler()
        scaler.fit(data_df[numeric_features])
        data_df[numeric_features] = scaler.transform(data_df[numeric_features])
    else:
        # 该方法区别于Zscore，可保存训练集中的均值、方差参数
        scaler = preprocessing.StandardScaler()
        scaler.fit(data_df[numeric_features])
        data_df[numeric_features] = scaler.transform(data_df[numeric_features])

    #
    # print(data_df.describe())
    return data_df


def ToRank(df, ascending):
    return df.rank(numeric_only=True, ascending=ascending)